import React, { Component } from 'react';
import { Chart, Axis, Tooltip, Geom, Label, Legend } from 'bizcharts';
import { DataView } from '@antv/data-set';
import ResizeObserver from 'resize-observer-polyfill';
import {
  themeLight,
  themeDark,
  commonConfig,
  axisConfig,
  legendConfig,
  fitFontSize,
  barConfig,
  lineConfig,
  pointConfig,
} from '../config';
import styles from '../index.less';

const {
  defaultTheme,
  defaultFontFamily,
  defaultTickCount,
  defaultChartUnitFontSize,
  defaultValueOffset,
  defaultValueFontSize,
  defaultPaddingTop,
} = commonConfig;
const { defaultXLabelFontSize, defaultXLabelOffset, defaultYLabelFontSize } = axisConfig;
const {
  defaultLegendOffsetY,
  defaultLegendFontSize,
  defaultLegendLineHeight,
  defaultLegendItemGap,
  defaultLegendMarkerCircle,
} = legendConfig;
const { defaultBarShape, defaultBarWidth, defaultBarOpacity } = barConfig;
const { defaultLineWidth } = lineConfig;
const { defaultPointSize } = pointConfig;

class BarAndLine extends Component {
  state = {
    height: 0,
    viewportScale: window.viewportScale,
  };

  componentDidMount() {
    this.resizeObserver();
    window.addEventListener('resize', this.resize, { passive: true });
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize);
  }

  handleRoot = n => {
    this.root = n;
  };

  handleRef = n => {
    this.node = n;
  };

  resize = () => {
    this.setState({ viewportScale: window.viewportScale });
  };

  resizeObserver() {
    const ro = new ResizeObserver(entries => {
      const { width, height } = entries[0].contentRect;
      this.setState(preState => {
        if (preState.width !== width || preState.height !== height) {
          return {
            height,
          };
        }
        return null;
      });
    });
    if (this.root) {
      ro.observe(this.root);
    }
  }

  render() {
    const { height: stateHeight, viewportScale } = this.state;
    const { theme = defaultTheme } = this.props;
    const {
      defaultColors,
      defaultColor,
      defaultChartUnitColor,
      defaultLineColor,
      defaultGridColor,
      defaultValueColor,
      defaultXLabelColor,
      defaultYLabelColor,
      defaultLegendColor,
    } = theme === 'dark' ? themeDark : themeLight;
    const {
      height: propsHeight,
      forceFit = true,
      data = [],
      padding = [defaultPaddingTop * viewportScale, 'auto', 'auto'],
      hiddenYLabel = false, // 是否隐藏y轴标签
      showValue = false, // 柱子顶端显示数值
      valueOffset = defaultValueOffset, // 柱状图数值距柱子顶端距离
      hiddenXLabel = false, // 是否隐藏x轴标签(包括轴线）
      hiddenXLabelValue = false, // 是否隐藏x轴标签
      xLabelOffset = defaultXLabelOffset, // x轴标签距x轴距离
      xLabelAutoRotate = false, // 是否自动旋转
      xLabelRotate = 0, // 旋转角度
      geomStyle = {}, // 为Geom添加自定义样式，设置cursor之类的

      groupField, // 分组字段（Legend）
      colors = defaultColors, // 分组颜色数组
      color = defaultColor,

      hiddenLegend = false, // 是否隐藏Legend
      legendOffsetY = defaultLegendOffsetY, // 垂直方向偏移
      legendFontSize = defaultLegendFontSize, // Legend字体大小
      legendColor = defaultLegendColor, // Legend字体颜色

      fieldExpand, // 字段展开中文名称对应
      x = 'x', // x轴字段名
      yLeftParams = {}, // 左侧y轴参数
      yRightParams = {}, // 右侧y轴参数
      showTooltip = true, // 显示tooltip
      showTooltipTitle = true, // 显示tooltip title
      adjust,
      shape = defaultBarShape,

      size = defaultBarWidth, // 每根柱子的宽度
      lineSize = defaultLineWidth, // 折线粗细
      showPoint = true, // 显示圆点

      chartUnitPosition, // 设置图表总单位位置
      chartUnitColor = defaultChartUnitColor, // 图表总单位颜色
      chartUnitFontSize = defaultChartUnitFontSize, // 图表总单位字体大小

      fontSize, // 同时设置以下几种字体大小，下面的设置将无效
      xLabelFontSize = defaultXLabelFontSize, // x轴标签字体大小
      yLabelFontSize = defaultYLabelFontSize, // y轴标签字体大小
      valueFontSize = defaultValueFontSize, // 图形上数值字体大小

      tickCount = defaultTickCount, // 网格线条数
      lineType, // 折线样式(dashed or line)

      xScale = {},
    } = this.props;

    const {
      y: yLeft = 'yLeft', // y轴字段名
      unit: unitLeft, // 单位
      showYUnit: showYUnitLeft, // y轴标签是否显示单位
      showTooltipUnit: showTooltipUnitLeft = true, // tooltip是否显示单位
      yScale: yScaleLeft = {},
    } = yLeftParams;

    const {
      y: yRight = 'yRight',
      unit: unitRight,
      showYUnit: showYUnitRight,
      showTooltipUnit: showTooltipUnitRight = true,
      yScale: yScaleRight = {},
    } = yRightParams;

    const scale = {
      [x]: {
        type: 'cat',
        ...xScale,
      },
      [yLeft]: {
        formatter(val) {
          return (val || val === 0) && (showYUnitLeft ? val.toFixed(0) + unitLeft : val.toFixed(0));
        },
        tickCount,
        ...yScaleLeft,
      },
      [yRight]: {
        formatter(val) {
          return (
            (val || val === 0) && (showYUnitRight ? val.toFixed(0) + unitRight : val.toFixed(0))
          );
        },
        tickCount,
        ...yScaleRight,
      },
    };

    // 字段展开
    let rows = [];
    if (fieldExpand && fieldExpand.length) {
      fieldExpand.map(({ field, fieldKey, rename }, index) => {
        const dv = new DataView();
        dv.source(data)
          .transform({
            type: 'rename',
            map: { [field]: rename },
          })
          .transform({
            type: 'fold',
            fields: [rename], // 展开字段集
            key: 'group', // key字段
            value: fieldExpand.length === 2 ? (index === 0 ? 'yLeft' : 'yRight') : fieldKey, // value字段
            retains: [x],
          });
        rows = [...rows, ...dv.rows];
      });
    }

    const height = propsHeight || stateHeight;

    return (
      <div
        className={styles.bar}
        style={{ height: height * viewportScale }}
        ref={this.handleRoot}
        key={this.root}
      >
        <div ref={this.handleRef}>
          <Chart
            scale={scale}
            height={height * viewportScale}
            forceFit={forceFit}
            data={fieldExpand ? rows : data}
            padding={padding}
            onTooltipChange={ev => {
              const { items } = ev; // tooltip显示的项
              const newItems = items.slice();
              if (newItems && newItems.length === 2) {
                items.splice(0);
                items.push({
                  ...newItems[0],
                  value:
                    newItems[0].value === 0 || newItems[0].value
                      ? unitLeft && showTooltipUnitLeft && !showYUnitLeft
                        ? newItems[0].value + unitLeft
                        : newItems[0].value
                      : '--',
                });
                items.push({
                  ...newItems[1],
                  value:
                    newItems[1].value === 0 || newItems[1].value
                      ? unitRight && showTooltipUnitRight && !showYUnitRight
                        ? newItems[1].value + unitRight
                        : newItems[1].value
                      : '--',
                });
              }
            }}
          >
            <Axis
              name={x}
              visible={!hiddenXLabel}
              line={{
                stroke: defaultLineColor,
              }}
              tickLine={null}
              label={
                hiddenXLabelValue
                  ? false
                  : {
                      autoRotate: xLabelAutoRotate,
                      rotate: xLabelRotate,
                      offset: xLabelOffset * viewportScale,
                      textStyle: {
                        fill: defaultXLabelColor,
                        fontSize: fitFontSize(fontSize || xLabelFontSize),
                        fontFamily: defaultFontFamily,
                      },
                    }
              }
            />
            <Axis
              name={yLeft}
              visible={!hiddenYLabel}
              label={{
                textStyle: {
                  fill: defaultYLabelColor,
                  fontSize: fitFontSize(fontSize || yLabelFontSize),
                  fontFamily: defaultFontFamily,
                },
              }}
              grid={{
                lineStyle: {
                  stroke: defaultGridColor,
                  lineWidth: 1,
                  // lineDash: [4, 0],
                },
                zeroLineStyle: {
                  stroke: defaultGridColor,
                },
              }}
            />
            <Axis
              name={yRight}
              visible={!hiddenYLabel}
              label={{
                textStyle: {
                  fill: defaultYLabelColor,
                  fontSize: fitFontSize(fontSize || yLabelFontSize),
                  fontFamily: defaultFontFamily,
                },
              }}
              grid={null}
            />
            <Legend
              visible={!hiddenLegend}
              position="top"
              offsetY={legendOffsetY * viewportScale}
              useHtml
              g2-legend-list-item={{
                lineHeight: `${defaultLegendLineHeight * viewportScale}px`,
                fontSize: `${legendFontSize * viewportScale}px`,
                fontFamily: defaultFontFamily,
                color: legendColor,
                marginRight: `${defaultLegendItemGap * viewportScale}px`,
                marginBottom: 0,
              }}
              g2-legend-marker={defaultLegendMarkerCircle}
            />
            <div>{showTooltip && <Tooltip showTitle={showTooltipTitle} crosshairs={false} />}</div>
            <Geom
              style={geomStyle}
              type="interval"
              position={`${x}*${yLeft}`}
              color={groupField ? [groupField, colors] : color}
              size={size * viewportScale}
              opacity={defaultBarOpacity}
              adjust={adjust}
              shape={shape}
            >
              {showValue && (
                <Label
                  content={yLeft}
                  offset={valueOffset * viewportScale}
                  textStyle={{
                    fill: defaultValueColor,
                    fontSize: fitFontSize(fontSize || valueFontSize),
                    fontFamily: defaultFontFamily,
                  }}
                />
              )}
            </Geom>
            <Geom
              style={{ ...geomStyle, lineDash: lineType === 'dashed' ? [4, 4] : [4, 0] }}
              type="line"
              position={`${x}*${yRight}`}
              color={groupField ? [groupField, colors] : color}
              shape="smooth"
              size={lineSize * viewportScale}
            >
              {showValue && (
                <Label
                  content={yRight}
                  offset={valueOffset * viewportScale}
                  textStyle={{
                    fill: defaultValueColor,
                    fontSize: fitFontSize(fontSize || valueFontSize),
                    fontFamily: defaultFontFamily,
                  }}
                />
              )}
            </Geom>
            <div>
              {showPoint && (
                <Geom
                  type="point"
                  position={`${x}*${yRight}`}
                  size={defaultPointSize * viewportScale}
                  shape="circle"
                  color={groupField ? [groupField, colors] : color}
                  tooltip={false}
                />
              )}
            </div>
          </Chart>
        </div>
        {!!chartUnitPosition && unitLeft && (
          <span
            className={styles.unit}
            style={{
              color: chartUnitColor,
              fontSize: chartUnitFontSize * viewportScale,
              left: 0,
              ...chartUnitPosition,
            }}
          >
            {`(${unitLeft})`}
          </span>
        )}
        {!!chartUnitPosition && unitRight && (
          <span
            className={styles.unit}
            style={{
              color: chartUnitColor,
              fontSize: chartUnitFontSize * viewportScale,
              right: 0,
              ...chartUnitPosition,
            }}
          >
            {`(${unitRight})`}
          </span>
        )}
      </div>
    );
  }
}

export default BarAndLine;
